package com.itmuch.cloud.study.util;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.deser.std.NumberDeserializers;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.apache.commons.lang.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;

public class JsonUtils {

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    static {
        OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 时间兼容
        OBJECT_MAPPER.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"));

        //属性匹配格式
        OBJECT_MAPPER.setPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CAMEL_CASE);

        // boolean转换增强
        SimpleModule booleanModule = new SimpleModule("BooleanConvertModule");
        booleanModule.addDeserializer(Boolean.class, new NumberDeserializers.BooleanDeserializer(Boolean.class, Boolean.FALSE));
        OBJECT_MAPPER.registerModule(booleanModule);

        // BigDecimal保持精度
        SimpleModule decimalModule = new SimpleModule("BigDecimalConvertModule");
        decimalModule.addSerializer(BigDecimal.class, new ToStringSerializer());
        OBJECT_MAPPER.registerModule(decimalModule);

        // 多余的字段不失败
        OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        OBJECT_MAPPER.configure(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS, true);

        OBJECT_MAPPER.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);

        OBJECT_MAPPER.configure(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT, true);

        //属性为NULL 不序列化
        OBJECT_MAPPER.setSerializationInclusion(Include.NON_NULL);
    }

    public static void toJson(Object object, OutputStream output) {
        if (object == null) {
            return;
        }

        try {
            OBJECT_MAPPER.writeValue(output, object);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static String toJson(Object object) {
        if (object == null) {
            return null;
        }

        try {
            return OBJECT_MAPPER.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public static <T> T toBean(String json, Class<T> clazz) {
        if (StringUtils.isBlank(json)) {
            return null;
        }

        try {
            return OBJECT_MAPPER.readValue(json, clazz);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    
    public static <T> T toBean(InputStream is, Class<T> clazz) {
        if (is == null) {
            return null;
        }

        try {
            return OBJECT_MAPPER.readValue(is, clazz);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    
    public static <T> T toBean(InputStream is, TypeReference<T> ref) {
        if (is == null) {
            return null;
        }

        try {
            return OBJECT_MAPPER.readValue(is, ref);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    public static <T> T toBean(String json, TypeReference<T> ref) {
        if (StringUtils.isBlank(json)) {
            return null;
        }

        try {
            return OBJECT_MAPPER.readValue(json, ref);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    
    @SuppressWarnings({ "unchecked" })
    public static <T> T toBean(String json, JavaType javaType) {
        if (StringUtils.isBlank(json)) {
            return null;
        }

        try {
            return (T) OBJECT_MAPPER.readValue(json, javaType);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static JavaType getCollectionType(Class<?> collectionClass, Class<?>... elementClasses) {
        return OBJECT_MAPPER.getTypeFactory().constructParametricType(collectionClass, elementClasses);
    }
    
    //返回格式化的JSON数据
    public static String toFormatedJson(Object bean) {
        if (bean == null) return null;
        return format(toJson(bean));
    }

    /**
     * 得到格式化json数据  退格用\t 换行用\r
     */
    public static String format(String jsonStr) {
        if (jsonStr == null || jsonStr == "") return null;
        int level = 0;
        StringBuffer jsonForMatStr = new StringBuffer();
        for (int i = 0; i < jsonStr.length(); i++) {
            char c = jsonStr.charAt(i);
            if (level > 0 && '\n' == jsonForMatStr.charAt(jsonForMatStr.length() - 1)) {
                jsonForMatStr.append(getLevelStr(level));
            }
            switch (c) {
                case '{':
                case '[':
                    jsonForMatStr.append(c + "\n");
                    level++;
                    break;
                case ',':
                    jsonForMatStr.append(c + "\n");
                    break;
                case '}':
                case ']':
                    jsonForMatStr.append("\n");
                    level--;
                    jsonForMatStr.append(getLevelStr(level));
                    jsonForMatStr.append(c);
                    break;
                default:
                    jsonForMatStr.append(c);
                    break;
            }
        }

        return jsonForMatStr.toString();

    }

    private static String getLevelStr(int level) {
        StringBuffer levelStr = new StringBuffer();
        for (int levelI = 0; levelI < level; levelI++) {
            levelStr.append("\t");
        }
        return levelStr.toString();
    }

}
